<?php
namespace core\models;
/**
 * 开发文档生成类
 * Class phpDoc
 * @author  ZhouHaoHua <53027327@qq.com> by 2014-09-01
 */
class Widget_phpDoc {
    /**
     * php文档输出目录
     * @var string
     */
    private $outputDir;

    public function __construct() {
        $this->outputDir = Z_APP . '/doc/phpDoc/';
    }

    /**
     * 取得文件输出目录
     * @return string
     */
    public function getOutputDir() {
        return $this->outputDir;
    }

    /**
     * 去除注释文字中的星号
     * @param string $str 源文字
     * @return 返回去星后的文字
     */
    public static function replaceAsterisk($str = '') {
        return str_replace(array('/**', '* ', '*/'), array('', '', ''), $str);
    }

    /**
     * 创建
     * @param array  $post    表单数组
     * @param string $message 提示信息【引用】
     * @return array 成功则返回生成后的数组信息
     */
    public function CreateData(array $post, &$message) {
        $error   = false;
        $data    = array();
        $message = '';
        //检测
        if ($post['source_file']) {
            if (file_exists($post['source_file'])) {
                require_once($post['source_file']);
            } else {
                $error   = true;
                $message = '文件：' . $post['source_file'] . '不存在！';
            }
        }
        if ($error == false && !$post['class_name']) {
            $error   = true;
            $message = '失败，没有输入类名！';
        }
        if ($error == false && !class_exists($post['class_name'])) {
            $error   = true;
            $message = '找不到类' . $post['class_name'] . '！';
        }
        //开始生成
        if ($error == false) {
            $ref                 = new \ReflectionClass($post['class_name']);
            $classDoc            = $ref->getDocComment();
            $data['classTitle']  = $classDoc;
            $data['className']   = $post['class_name'];
            $data['classFile']   = $ref->getFileName();
            $data['classUpdate'] = date('Y-m-d H:i:s', filemtime($data['classFile']));
            $data['classInit']   = '需要输入什么变音吗？';

            //属性
            $pubicProperties = $ref->getProperties(\ReflectionProperty::IS_PUBLIC);
            foreach ($pubicProperties as $O) {
                $PropertiesDoc             = $ref->getProperty($O->name)->getDocComment();
                $data['classProperties'][] = array(
                    'name' => $O->name, 'type' => '', 'doc' => $PropertiesDoc, 'is_static' => $O->isStatic()
                );
            }
            if ($data['classProperties']) {//属性排序
                $this->sort($data['classProperties']);
            }
            //方法
            $Methods = $ref->getMethods(\ReflectionMethod::IS_STATIC | \ReflectionMethod::IS_PUBLIC);
            foreach ($Methods as $MO) {
                $MethodsDoc        = $MO->getDocComment();
                $MethodsParameters = $MO->getParameters();
                $parameters        = array();
                $loop              = 0;
                foreach ($MethodsParameters as $P) {
                    $parameters[$loop] = '$' . $P->getName();
                    if ($P->isPassedByReference()) { //判断是否是引用
                        $parameters[$loop] = '<b>&</b>' . $parameters[$loop];
                    }

                    if ($P->isOptional()) { //$P->allowsNull()
                        $_dv = $P->getDefaultValue();
                        switch (true) { //根据不同类型设置对应的默认值
                            case is_array($_dv):
                                if ($_dv) {
                                    $parameters[$loop] .= ' = ' . print_r($_dv, true); // . $_dv;
                                } else {
                                    $parameters[$loop] .= ' = array()';
                                }
                                break;
                            case is_string($_dv):
                                $parameters[$loop] .= ' = "' . $_dv . '" ';
                                break;
                            case is_numeric($_dv):
                                $parameters[$loop] .= ' = ' . $_dv . ' ';
                                break;
                            case is_object($_dv):
                                $parameters[$loop] .= ' = ' . get_class($_dv) . ' ';
                                break;
                            case is_bool($_dv):
                                $parameters[$loop] .= ' = ' . ($_dv ? 'true' : 'false') . ' ';
                                break;
                            default:
                                $parameters[$loop] .= ' = "' . $_dv . '" ';
                                break;
                        }
                        $parameters[$loop] = '[' . $parameters[$loop] . ']';
                    }
                    $loop++;
                }
                $data['classMethods'][] = array(
                    'name'      => $MO->getName(), 'parameters' => $parameters, 'doc' => $MethodsDoc,
                    'is_static' => $MO->isStatic()
                );
            }
            if ($data['classMethods']) {//方法排序
                $this->sort($data['classMethods']);
            }
            //natcasesort();
            /*echo '<pre>';
            var_dump($Methods);
            echo '</pre>';
            exit;*/
        }
        return ($error === false ? $data : false);
    }


    /**
     * 创建简单格式的注释
     * @param string $class_name 类名
     * @param string $exclude 排除哪些公开的方法。私有的方法已经排除！
     * @return array|mixed
     * @author ZhouHaoHua <53027327@qq.com>
     */
    function createSimple($class_name, $exclude = '__construct, __destruct, __call, __callStatic, __get, __set, __isset, __unset, __sleep, __wakeup, __toString, __invoke, __set_state, __clone, __debugInfo') {

        $ref                 = new \ReflectionClass($class_name);
        $classDoc            = $ref->getDocComment();
        $data['classTitle']  = $classDoc;
        $data['className']   = $class_name;
        $data['classFile']   = $ref->getFileName();
        $data['classUpdate'] = date('Y-m-d H:i:s', filemtime($data['classFile']));
        $data['classInit']   = '需要输入什么变音吗？';

        //属性
        $pubicProperties = $ref->getProperties();
        foreach ($pubicProperties as $O) {
            if ($O->class != $class_name) {

                continue;
            }
            $PropertiesDoc             = $ref->getProperty($O->name)->getDocComment();
            $data['classProperties'][] = array(
                'name' => $O->name, 'type' => '', 'doc' => $PropertiesDoc, 'is_static' => $O->isStatic()
            );
        }
        if ($data['classProperties']) {//属性排序
            $this->sort($data['classProperties']);
        }
        //方法
        $Methods = $ref->getMethods(\ReflectionMethod::IS_STATIC | \ReflectionMethod::IS_PUBLIC);
        $excludeArray = explode(',', $exclude);
        $excludeArray = \Z::Arrays()->trim($excludeArray);
        foreach ($Methods as $MO) {
            $MethodsDoc = $MO->getDocComment();
            $MethodName = $MO->getName();
            if(in_array($MethodName, $excludeArray)){   //跳过被排除的方法
                continue;
            }
            if ($MO->class != $class_name) {
                continue;
            }
            $MethodsDoc = str_replace('\r', '', $MethodsDoc);
            //            echo $MethodsDoc,'<br />';
            $MethodsParameters = $MO->getParameters();
            $parameters        = array();
            $loop              = 0;
            foreach ($MethodsParameters as $P) {
                $parameters[$loop] = '$' . $P->getName();
                if ($P->isPassedByReference()) { //判断是否是引用
                    $parameters[$loop] = '<b>&</b>' . $parameters[$loop];
                }

                if ($P->isOptional()) { //$P->allowsNull()
                    $_dv = $P->getDefaultValue();
                    switch (true) { //根据不同类型设置对应的默认值
                        case is_array($_dv):
                            if ($_dv) {
                                $parameters[$loop] .= ' = ' . print_r($_dv, true); // . $_dv;
                            } else {
                                $parameters[$loop] .= ' = array()';
                            }
                            break;
                        case is_string($_dv):
                            $parameters[$loop] .= ' = "' . $_dv . '" ';
                            break;
                        case is_numeric($_dv):
                            $parameters[$loop] .= ' = ' . $_dv . ' ';
                            break;
                        case is_object($_dv):
                            $parameters[$loop] .= ' = ' . get_class($_dv) . ' ';
                            break;
                        case is_bool($_dv):
                            $parameters[$loop] .= ' = ' . ($_dv ? 'true' : 'false') . ' ';
                            break;
                        default:
                            $parameters[$loop] .= ' = "' . $_dv . '" ';
                            break;
                    }
                    $parameters[$loop] = '[' . $parameters[$loop] . ']';
                }
                $loop++;
            }
            $data['classMethods'][] = array(
                'name'      => $MethodName, 'parameters' => $parameters, 'doc' => $MethodsDoc,
                'is_static' => $MO->isStatic()
            );
        }
        if ($data['classMethods']) {//方法排序
            $this->sort($data['classMethods']);
        }
        return $data;
    }

    /**
     * 把注释整理合并成想要的结果
     * @param        $arr
     * @param        $message
     * @param string $begin
     * @param string $out_put
     */
    function createMyNeed($arr, &$message, $begin = '2.1', $out_put = 'F:/') {
        if (!is_dir($out_put)) {
            $message = ('目录不对');
            return;
        }


        $deep   = 1;
        $first  = $this->createTab($deep);
        $return = $first . $begin . ' ' . $arr['className'] . '(' . $this->getName($arr['classTitle']) . ')' . "\r\n";
        $second = $this->createTab(2);
        $return .= $second . $begin . '.1 常量：' . "\r\n";
        $three = $this->createTab(3);
        foreach ($arr['classProperties'] as $key => $value) {
            $return .= $three . $value['name'] . '：' . $this->getName($value['doc']) . "\r\n";
        }
        $return .= $second . $begin . '.2 方法：' . "\r\n";
        foreach ($arr['classMethods'] as $key => $value) {
            $param = $this->createParam($value['doc']);

            $return .= $three . $value['name'] . '：' . $this->getName($value['doc']) . "\r\n";
            if ($param) {
                $return .= $three . '   参数：' . "\r\n";
                foreach ($param as $v)
                    $return .= $three . '       ' . $v['name'] . ' ' . $v['type'] . ' ' . $v['desc'] . "\r\n";
            }

            $return_doc = $this->createReturn($value['doc']);
            if ($return_doc) {
                $return .= $three . '   返回：' . $return_doc['type'] . ' ' . $return_doc['name'] . "\r\n";
            }

        }
        file_put_contents($out_put . '/' . $arr['className'] . '.txt', $return);
    }

    /**
     * 输出CSV格式
     * @param        $arr
     * @param        $message
     * @param string $out_put
     */
    function createCSV($arr, &$message, $out_put = 'F:/') {
        if (!is_dir($out_put)) {
            $message = ('目录不对');
            return;
        }
    }

    /**
     * 生成$number个TAB
     * @param $number
     * @return string
     */
    function createTab($number) {
        $return = '';
        for (; $number > 0; $number--) {
            $return .= '   ';
        }
        return $return;
    }

    /**
     * 获取名称
     * @param $str
     * @return string
     */
    function getName($str) {
        preg_match('/\/\*\*([\s\S])*?(\* \@|\*\/)/', $str, $tmp);
        $str = $tmp[0];
        $str = preg_replace('/[\/\*@ ]/', '', $str);
        return trim($str);
    }

    /**
     * 处理参数
     * @param $str
     * @return mixed
     */
    function createParam($str) {
        if (preg_match_all('/@param(.)*\r\n/', $str, $tmp)) {
            foreach ($tmp[0] as $key => $value) {
                $value   = preg_replace('/\s{2,}/', ' ', $value);
                $tmp1    = explode(' ', trim($value));
                $tmp_tmp = '';
                if ($tmp_tmp = trim($tmp1[1]) && $tmp_tmp[0] == '$') {
                    $return[$key]['name'] = trim($tmp1[1]);
                    $return[$key]['type'] = '';
                    $begin                = 2;
                } elseif ($tmp_tmp = trim($tmp1[2]) && $tmp_tmp[0] == '$') {
                    $begin                = 3;
                    $return[$key]['name'] = trim($tmp1[2]);
                    $return[$key]['type'] = trim($tmp1[1]);
                } else {
                    print_r($tmp1);
                }
                $j = count($tmp1);
                for (; $begin < $j; $begin++) {
                    $return[$key]['desc'] .= ' ' . $tmp1[$begin];
                }
            }
        }
        return $return;
    }

    /**
     * 处理返回值
     * @param $str
     * @return array
     */
    function createReturn($str) {
        $return = array();
        if (preg_match('/@return(.)*\r\n/', $str, $tmp)) {
            $str  = preg_replace('/\s{2,}/', ' ', $tmp[0]);
            $tmp1 = explode(' ', trim($str));
            if (isset($tmp1[1])) {
                if (preg_match('/[a-zA-Z]{1,}/', trim($tmp1[1]), $tmp2) && $tmp2[0] == trim($tmp1[1])) {
                    $return['type'] = $tmp1[1];
                    $return['name'] = $tmp1[2];
                } else {
                    $return['name'] = $tmp1[1];
                    $return['type'] = '';
                }
            }
        }
        return $return;
    }

    /**
     * 缓存区开始
     * @author ZhouHaoHua <53027327@qq.com>
     */
    public function ob_start() {
        ob_start();
    }

    /**
     * 取得缓存内容并写文件
     * @param $post 表单变
     *              量
     * @return string 返回文件内容
     * @author ZhouHaoHua <53027327@qq.com>
     */
    public function ob_get_clean($post) {
        $text = ob_get_clean();
        //把文件名中的_换成/输出
        $filesName = $this->getOutputDir() . str_replace('_', '/', $post['class_name']) . '.html';
        //echo dirname($filesName);exit;
        @mkdir(dirname($filesName), 0777, true);
        file_put_contents($filesName, $text);
        return $text;
    }

    /**
     * 属性和方法排序
     * @param array $array , 此数组第二维必须包含有'name'
     */
    private function sort(array &$array) {
        foreach ($array as $key => $row) {
            $volume[$key] = $row['name'];
        }
        // 将数据根据 name 升序排列
        // 把 $array 作为最后一个参数，以通用键排序
        array_multisort($volume, SORT_ASC, $array);
    }

    /**
     * 获取目录下所有的类名称
     * @param        $dir_name
     * @param string $class_name
     * @return array|string
     */
    function getDirAllClass($dir_name, $class_name = '') {

        if (!is_dir($dir_name)) {
            return '所传不是目录';
        }
        $need     = array();
        $dir_hand = dir($dir_name);
        while ($file = $dir_hand->read()) {
            //            echo "$dir_name/$file==".DIR_FS_CATALOG.'includes/classes/nusoaplib<br />';
            //            if("$dir_name/$file"==DIR_FS_CATALOG.'includes/classes/nusoaplib')

            if ($file == '.' || $file == '..' || "$dir_name/$file" == DIR_FS_CATALOG . 'includes/classes/order/products' || "$dir_name/$file" == DIR_FS_CATALOG . 'includes/classes/nusoaplib')
                continue;
            if (is_dir("$dir_name/$file")) {
                //                $tmp=$this->getDirAllClass("$dir_name/$file",$class_name?$class_name.'_'.$file:$file);
                //                $need=array_merge($tmp,$need);
            } else {
                if ($class_name_need = $this->checkIsClass("$dir_name/$file"))
                    $need[] = $class_name_need;
            }
        }
        return $need;
    }

    /**
     * 检查是否是类
     * @param $class_name
     * @return bool
     */
    function checkIsClass($class_name) {
        if (count($tmp = explode('.', $class_name)) > 2) {
            return false;
        } elseif ($tmp[1] != 'php') {
            return false;
        }//(?<!//)(?<!(\s*)\*(\s*))(\s*)class(\s+)(\w*)(\s*|\r\n){
        //        echo file_get_contents($class_name);
        preg_match('/(?<!\/\/)(?<!\*)(\s*)((abstract(\s+)class)|(final(\s+)class)|((\s+)class)|((\s+)interface))(\s+)(\w*)(\s*|\r\n)|((\s+)extends|implements(\s+)(\w*)){/', file_get_contents($class_name), $match);
        if ($match[12]) {
            return $match[12];
        } else {
            return false;
        }

    }
}